using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.IO.Ports;

namespace SerialPortToKeyboard
{
	public partial class MainForm : Form
	{
		private SerialPort serialPort;
		private static char [] SendKeysSpecialChars = { '+', '^', '%', '~', '(', ')', '{', '}', '[', ']' };

		public MainForm()
		{
			serialPort = new SerialPort();
			serialPort.ReadTimeout = 1000;

			InitializeComponent();
			InitializeSerialPortControls();
			LoadDefaultSettings();

			serialPort.DataReceived += new SerialDataReceivedEventHandler(OnDataReceived);
		}

		private void InitializeSerialPortControls()
		{
			comboBoxPortName.Items.Clear();
			comboBoxPortName.Items.AddRange(SerialPort.GetPortNames());

			comboBoxParity.Items.Clear();
			comboBoxParity.Items.AddRange(Enum.GetNames(typeof(Parity)));

			comboBoxStopBits.Items.Clear();
			comboBoxStopBits.Items.AddRange(Enum.GetNames(typeof(StopBits)));

			comboBoxHandshake.Items.Clear();
			comboBoxHandshake.Items.AddRange(Enum.GetNames(typeof(Handshake)));

			comboBoxDataBits.Items.Clear();
			comboBoxDataBits.Items.AddRange(new string[] { "6", "7", "8", "9" });

			comboBoxBaudRate.Items.Clear();
			comboBoxBaudRate.Items.AddRange(new string[] { "1200", "2400", "4800", "9600", "14400", "19200", "38400", "57600", "115200" });
		}

		private void OnDataReceived(object sender, SerialDataReceivedEventArgs e)
		{
			string line;
			try
			{
				line = (sender as SerialPort).ReadLine() + Environment.NewLine;
			}
			catch (TimeoutException)
			{
				line = (sender as SerialPort).ReadExisting();
			}
			AppendLogText(line, Color.Blue);

			if (this.PassDataToKeyboard)
			{
				SendKeys.SendWait(EncloseSendKeysString(line));
			}
		}

		protected override void OnLoad(EventArgs e)
		{
			WindowState = FormWindowState.Minimized;
			ShowInTaskbar = false;

			base.OnLoad(e);

			buttonTogglePort_Click(this, e);
		}

		delegate void AppendLogTextCallback(string text, Color color);
		private void AppendLogText(string text, Color color)
		{
			if (LogData)
			{
				if (richTextBox.InvokeRequired)
				{
					AppendLogTextCallback d = new AppendLogTextCallback(AppendLogText);
					this.Invoke(d, new object[] { text, color });
					return;
				}

				richTextBox.SelectionStart = richTextBox.TextLength;
				richTextBox.SelectionLength = 0;

				richTextBox.SelectionColor = color;
				try
				{
					richTextBox.AppendText(text);
				}
				catch (Exception)
				{ }
				richTextBox.SelectionColor = richTextBox.ForeColor;
			}
		}

		private void buttonTogglePort_Click(object sender, EventArgs e)
		{
			if (serialPort.IsOpen)
			{
				serialPort.Close();
				buttonTogglePort.Text = "Open Port";
				buttonSend.Enabled = false;
				groupBoxSettings.Enabled = true;
				AppendLogText("Serial port closed." + Environment.NewLine, Color.White);
			}
			else
			{
				serialPort.Open();
				buttonTogglePort.Text = "Close Port";
				buttonSend.Enabled = true;
				groupBoxSettings.Enabled = false;
				AppendLogText("Serial port opened." + Environment.NewLine, Color.White);
			}
		}

		private void buttonSend_Click(object sender, EventArgs e)
		{
			AppendLogText("< " + textBoxSend.Text + Environment.NewLine, Color.Green);
			serialPort.WriteLine(textBoxSend.Text);
			textBoxSend.Text = "";
		}

		private string EncloseSendKeysString(string value)
		{
			List<char> sendKeysSpecialChars = new List<char>(SendKeysSpecialChars);
			string result = "";

			for (int i = 0; i < value.Length; i++)
			{
				if (sendKeysSpecialChars.Contains(value[i]))
				{
					result += "{" + value[i] + "}";
				}
				else
				{
					result += value[i];
				}
			}

			return result;
		}

		private void comboBoxPortName_SelectedValueChanged(object sender, EventArgs e)
		{
			if (comboBoxPortName.SelectedValue != null)
			{
				serialPort.PortName = this.PortName;
			}
		}

		private void comboBoxBaudRate_SelectedValueChanged(object sender, EventArgs e)
		{
			if (comboBoxBaudRate.SelectedValue != null)
			{
				serialPort.BaudRate = this.BaudRate;
			}
		}

		private void comboBoxParity_SelectedValueChanged(object sender, EventArgs e)
		{
			if (comboBoxParity.SelectedValue != null)
			{
				serialPort.Parity = this.Parity;
			}
		}

		private void comboBoxStopBits_SelectedValueChanged(object sender, EventArgs e)
		{
			if (comboBoxStopBits.SelectedValue != null)
			{
				serialPort.StopBits = this.StopBits;
			}
		}

		private void comboBoxDataBits_SelectedValueChanged(object sender, EventArgs e)
		{
			if (comboBoxDataBits.SelectedValue != null)
			{
				serialPort.DataBits = this.DataBits;
			}
		}

		private void comboBoxHandshake_SelectedValueChanged(object sender, EventArgs e)
		{
			if (comboBoxHandshake.SelectedValue != null)
			{
				serialPort.Handshake = this.Handshake;
			}
		}

		private void LoadDefaultSettings()
		{
			if (comboBoxPortName.Items.Count > 0)
			{
				this.PortName = Properties.Settings.Default.PortName;
				this.Parity = Properties.Settings.Default.Parity;
				this.DataBits = Properties.Settings.Default.DataBits;
				this.BaudRate = Properties.Settings.Default.BaudRate;
				this.StopBits = Properties.Settings.Default.StopBits;
				this.Handshake = Properties.Settings.Default.Handshake;
				this.LogData = Properties.Settings.Default.LogData;
				this.PassDataToKeyboard = Properties.Settings.Default.PassDataToKeyboard;
			}
		}

		private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
		{
			Properties.Settings.Default.PortName = this.PortName;
			Properties.Settings.Default.Parity = this.Parity;
			Properties.Settings.Default.DataBits = this.DataBits;
			Properties.Settings.Default.BaudRate = this.BaudRate;
			Properties.Settings.Default.StopBits = this.StopBits;
			Properties.Settings.Default.Handshake = this.Handshake;
			Properties.Settings.Default.LogData = this.LogData;
			Properties.Settings.Default.PassDataToKeyboard = this.PassDataToKeyboard;
			Properties.Settings.Default.Save();

			notifyIcon.Visible = false;
		}

		private string PortName
		{
			get
			{
				return comboBoxPortName.SelectedItem as string;
			}
			set
			{
				comboBoxPortName.SelectedItem = value;
			}
		}

		private int BaudRate
		{
			get
			{
				return int.Parse(comboBoxBaudRate.SelectedItem as string);
			}
			set
			{
				comboBoxBaudRate.SelectedItem = value.ToString();
			}
		}

		private Parity Parity
		{
			get
			{
				return (Parity)Enum.Parse(typeof(Parity), comboBoxParity.SelectedItem as string);
			}
			set
			{
				comboBoxParity.SelectedItem = value.ToString();
			}
		}

		private StopBits StopBits
		{
			get
			{
				return (StopBits)Enum.Parse(typeof(StopBits), comboBoxStopBits.SelectedItem as string);
			}
			set
			{
				comboBoxStopBits.SelectedItem = value.ToString();
			}
		}

		private int DataBits
		{
			get
			{
				return int.Parse(comboBoxDataBits.SelectedItem as string);
			}
			set
			{
				comboBoxDataBits.SelectedItem = value.ToString();
			}
		}

		private Handshake Handshake
		{
			get
			{
				return (Handshake)Enum.Parse(typeof(Handshake), comboBoxHandshake.SelectedItem as string);
			}
			set
			{
				comboBoxHandshake.SelectedItem = value.ToString();
			}
		}

		private bool LogData
		{
			get
			{
				return toggleLogToolStripMenuItem.Checked;
			}
			set
			{
				toggleLogToolStripMenuItem.Checked = value;
			}
		}

		private bool PassDataToKeyboard
		{
			get
			{
				return checkBoxPassDataToKeyboard.Checked;
			}
			set
			{
				checkBoxPassDataToKeyboard.Checked = value;
			}
		}

		private void ToggleWindow()
		{
			if(WindowState == FormWindowState.Minimized)
			{
				WindowState = FormWindowState.Normal;
				menuItemShow.Text = "Hide";
			}
			else
			{
				WindowState = FormWindowState.Minimized;
				menuItemShow.Text = "Show";
			}
		}

		private void menuItemExit_Click(object sender, EventArgs e)
		{
			Application.Exit();
		}

		private void menuItemToggle_Click(object sender, EventArgs e)
		{
			ToggleWindow();
		}

		private void notifyIcon_DoubleClick(object sender, EventArgs e)
		{
			ToggleWindow();
		}

		private void toggleLogToolStripMenuItem_CheckStateChanged(object sender, EventArgs e)
		{
			toggleLogToolStripMenuItem.Text = LogData ? "Enabled" : "Disabled";
		}

		private void clearLogToolStripMenuItem_Click(object sender, EventArgs e)
		{
			richTextBox.Text = "";
		}
	}
}
